/**
 * 游戏时间和开服天数
 * 1. 可以reset_time来改变服务器的时间为任意时间（并没有改机器时间）
 * 2. 可以reset_openday改变开服时间为任意时间
 * 这样方便测试，并且提供一些基础函数和很多今日函数！
 */

// import { strict as assert } from 'assert';
// import { EventObj } from './EventObj';
// import { getLogger } from './log';
// import {
// 	parseTimeYearMonthDay,
// 	parseTimeHourMin,
// 	getDateByString,
// 	oneDay,
// 	getWeekInMonthDate,
// 	timeDiffDay,
// } from './TimeUtils';
// 下面会导致循环引用！
// import { getTimex } from "./timex";

function pad2(n: number): string {
	return n < 10 ? `0${n}` : `${n}`;
}

class GameTime extends EventObj {
	passed = 0; // 毫秒

	setPassed(time: number): void {
		this.passed = time;
		this.updateDate('setPassed');
		this.onTimeReset('date', this.getTimems());
	}

	// 恢复到正常时间
	restoreDateTime(): void {
		this.setPassed(0);
	}

	rawTime(): number {
		return new Date().getTime();
	}

	resetDate(d: Date): void {
		this.resetTime(d.getTime());
	}

	// reset day not change time!
	resetSDate(sdate: string): void {
		const [year, month, day] = parseTimeYearMonthDay(sdate);
		const d = this.getDate();
		this.resetDate(
			new Date(year, month - 1, day, d.getHours(), d.getMinutes(), d.getSeconds()),
		);
	}

	resetTime(time: number): void {
		const now = this.rawTime();
		this.passed = time - now;
		this.updateDate('resetTime');
		this.onTimeReset('date', this.getTimems());
	}

	// 设置当前时间，并不改变天数
	resetClock(hour: number, min: number, sec: number): void {
		const d = this.getDate();
		d.setHours(hour, min, sec);
		this.resetDate(d);
	}

	resetSClock(sclock: string): void {
		const [h, m, s] = parseTimeHourMin(sclock);
		this.resetClock(h, m, s);
	}

	// 使用真实世界的今天的时间
	resetToday(hour: number, min: number, sec: number): void {
		const d = new Date();
		d.setHours(hour, min, sec);
		this.resetDate(d);
	}

	resetStoday(stime: string): void {
		const [hour, min, sec] = parseTimeHourMin(stime);
		this.resetToday(hour, min, sec);
	}

	// 睡觉到该时间
	sleep(hour: number): void {
		if (hour < 0 || hour >= 24) {
			return;
		}
		this.resetToday(hour, 0, 0);
	}

	// 当前时间，服务器可以通过reset_time来改变这个时间以便调试！
	// 修改了timex.ts, gd_activity.ts,player_counter.ts 其他的没有改！
	getTimems(): number {
		// return this.get_date().getTime();
		return this.rawTime() + this.passed;
	}

	toLocaleString(): string {
		return this.getDate().toLocaleString();
	}

	toLocaleTimeString(): string {
		return this.getDate().toLocaleTimeString();
	}

	toLocaleDateString(): string {
		return this.getDate().toLocaleDateString();
	}

	getDate(): Date {
		return new Date(this.getTimems());
	}

	year = 0;

	month = 0;

	wDay = 0; // week day 1..7

	mDay = 0; // month day 1..30

	// absDay = 0; // day from 2000-01-01
	// absweek = 0;

	dateStr = '';

	updateTime = new Date();
	// hour = 0;
	// min = 0;
	// sec = 0;

	openTime = new Date(); // 开服的时间

	constructor() {
		super();
		// let info = get_gameinfo(get_game_id())
		// this.setOpenTime(new Date(info && info.open_time))
		// if (info)
		//    this.openDate = new Date(info.open_time)
		this.updateDate('construtor');
	}

	getRunms(): number {
		return new Date().getTime() - this.openTime.getTime();
	}

	onDay(): void {
		this.updateDate('onDay');
	}

	updateDate(where: string): void {
		const date = this.getDate();
		this.updateTime = date;
		this.year = date.getFullYear();
		this.month = date.getMonth() + 1; // 1-12
		this.wDay = date.getDay();
		// 按照中国的传统周日是周末
		if (this.wDay === 0) {
			this.wDay = 7;
		}
		this.mDay = date.getDate(); // 1-31
		// this.absDay = getAbsDay2000(date);
		// this.absweek = GetAbsWeek2000(date);

		this.dateStr = this.getDayStr();
		this.updateOpenTime(where);
	}

	private openedday = 0;
	// openedweek = 0;

	openDate: Date = new Date();

	setOpenTime(opentime: Date): void {
		this.openDate = opentime;
		this.updateOpenTime('setOpenTime');
	}

	protected updateOpenTime(where: string): void {
		// const openAbsday = getAbsDay2000(this.openDate);
		this.openedday = timeDiffDay(this.openDate, this.getDate());
		// this.openedday = this.absDay - openAbsday + 1; // 开服从1开始计数
		getLogger('game').warn('GameTime.updateOpenTime', where);
	}

	// 以下均返回毫秒！
	// '2018.11.19-12:00'
	getTimeToDate(start: string): number {
		const date = getDateByString(start);
		return date.getTime() - this.getDate().getTime();
	}

	// "12:40" 返回毫秒
	getTimeToToday(start: string): number {
		const [hour, min, sec] = parseTimeHourMin(start);
		return this.getTimeToHourMin(hour, min, sec);
	}

	// 返回毫秒
	getTimeToHourMin(hour: number, min: number, sec = 0): number {
		const date = this.getDate();
		return (
			(hour - date.getHours()) * 60 * 60 * 1000
			+ (min - date.getMinutes()) * 60 * 1000
			+ (sec - date.getSeconds()) * 1000
		);
	}

	resetOpentime(opentime: Date): void {
		this.setOpenTime(opentime);
		this.onTimeReset('open', this.getOpenedday());
	}

	resetSOpentime(stime: string): void {
		const date = new Date(stime);
		this.resetOpentime(date);
	}

	resetOpenday(day: number): void {
		assert(day > 0);
		let d = this.getTimems();
		d -= (day - 1) * oneDay;
		this.resetOpentime(new Date(d));
	}

	getOpenedday(): number {
		return this.openedday;
	}

	getDayStr(): string {
		const date = this.getDate();
		return `${pad2(date.getMonth() + 1)}.${pad2(date.getDate())}`;
	}

	getTimeStr(): string {
		const date = this.getDate();
		return `${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
	}

	// 1-7 礼拜几？把礼拜天改为7
	getWday(): number {
		const wday = this.getDate().getDay();
		return wday === 0 ? 7 : wday;
	}

	// 1-31
	getMday(): number {
		return this.getDate().getDate();
	}

	// 1-5 本月的第几周，
	getWMonth(): number {
		return getWeekInMonthDate(this.getDate());
	}
	// onTimeResetFunc: Function; // timex.onTimeReset

	onTimeReset(event: string, data: number): void {
		getLogger('game').warn(
			`GameTime.onTimeReset from:${event},now:${this.toLocaleString()},open:${this.openDate.toLocaleDateString()},opened:${this.getOpenedday()}`,
		);
		// if (this.onTimeResetFunc) this.onTimeResetFunc(where);
		this.emit(event, data);
	}

	private static instance: GameTime;

	public static ins(): GameTime {
		if (!this.instance) {
			this.instance = new GameTime();
		}
		return this.instance;
	}
}

function getServerDate(): Date {
	return GameTime.ins().getDate();
}

function getServerTimems(): number {
	return GameTime.ins().getTimems();
}

// for mysql db!
function getServerTimeSec(): number {
	return Math.floor(GameTime.ins().getTimems() / 1000);
}

function getOpenedday(): number {
	return GameTime.ins().getOpenedday();
}

function isTopOftheHour(date: Date = getServerDate()): boolean {
	console.log('分钟', date.getMinutes());
	return true;
}
